package net.aiion.weave.impl.xml;

import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import net.aiion.weave.spi.xml.XmlContext;
import net.aiion.weave.spi.xml.XmlGenerator;
import net.aiion.weave.spi.xml.XmlSink;
import net.aiion.weave.spi.xml.XmlSourceFactory;
import net.aiion.weave.spi.xml.events.XmlEvent;

public class XmlContextImpl implements XmlContext
{
    private final Map<String, Object> properties;

    private List<XmlSink> xmlSinks;

    private int currentXmlSinkIndex;
    private XmlEvent currentXmlEvent;

    private XmlGenerator xmlGenerator;

    public XmlContextImpl() {
        properties = new HashMap<>();
    }

    public void setXmlSinks(final List<XmlSink> aXmlSinks) {
        xmlSinks = aXmlSinks;
    }

    @Override
    public void setProperty(final String aName, final Object aValue) {
        properties.put(aName, aValue);
    }

    @Override
    public void removeProperty(final String aName) {
        properties.remove(aName);
    }

    @Override
    public Object getProperty(final String aName) {
        return properties.get(aName);
    }

    @Override
    public <T> T getProperty(final String aName, final Class<T> aType) {
        final Object o = getProperty(aName);
        if (aType.isInstance(o)) {
            return aType.cast(o);
        }

        return null;
    }

    @Override
    public XmlEvent getXmlEvent() {
        return currentXmlEvent;
    }

    @Override
    public XmlGenerator getXmlGenerator() {
        if (xmlGenerator == null) {
            xmlGenerator = XmlSourceFactory.getInstance().createGenerator();
            xmlGenerator.addSink(new DispatchingXmlSink());
        }

        return xmlGenerator;
    }

    @Override
    public void forwardXmlEvent(final XmlEvent aXmlEvent) {
        if (currentXmlSinkIndex < xmlSinks.size()) {
            final XmlEvent oldXmlEvent = currentXmlEvent;
            currentXmlEvent = aXmlEvent;

            xmlSinks.get(currentXmlSinkIndex++).handleXmlEvent(this);

            currentXmlEvent = oldXmlEvent;
            currentXmlSinkIndex--;
        }
    }

    @Override
    public void forwardXmlEvents(final Collection<XmlEvent> aXmlEvents) {
        for (XmlEvent xmlEvent : aXmlEvents) {
            forwardXmlEvent(xmlEvent);
        }
    }

    private final class DispatchingXmlSink implements XmlSink
    {
        public DispatchingXmlSink() {
        }

        @Override
        public void handleXmlEvent(final XmlContext aXmlContext) {
            forwardXmlEvent(aXmlContext.getXmlEvent());
        }
    }
}
